﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Diagnostics.Contracts;
using Platform.Infrastructure;

namespace System.Linq.Expressions
{
	internal sealed class ReplaceExpressionVisitor<TFilter> : ExpressionVisitor
		where TFilter : Expression
	{

		public ReplaceExpressionVisitor( Func<TFilter, bool> where, Func<TFilter, Expression> changeBy )
		{
			Arguments.IsNotNull( "where", where );

			this.Where = where;
			this.ChangeBy = changeBy;
		}


		public Func<TFilter, bool> Where
		{ get; private set; }

		public Func<TFilter, Expression> ChangeBy
		{ get; private set; }

		protected override Expression VisitBinary( BinaryExpression node )
		{
			TFilter filterNode = node as TFilter;

			if ( filterNode != null
				&& this.Where( filterNode ) )
				return this.ChangeBy( filterNode ) ?? Expression.Default( typeof( object ) );

			return base.VisitBinary( node );
		}

		public override Expression Visit( Expression node )
		{
			if ( node is TFilter )
				if ( this.Where( ( TFilter )node ) )
					return this.ChangeBy( ( TFilter )node ) ?? Expression.Default( typeof( object ) );

			return base.Visit( node );
		}
	}


	public static class ExchangeExtensions
	{

		public static Expression Replace<TExpression>( this Expression node, Expression replaceBy )
			where TExpression : Expression
		{
			return node.Replace<TExpression>( null, replaceBy );
		}

		public static Expression Replace<TExpression>( this Expression node, Func<TExpression, bool> where, Expression replaceBy )
			where TExpression : Expression
		{
			return node.Replace<TExpression>( where, x => replaceBy );
		}

		public static Expression Replace<TExpression>( this Expression node, Func<TExpression, bool> predicate, Func<TExpression, Expression> replacer )
			where TExpression : Expression
		{
			var visitor = new ReplaceExpressionVisitor<TExpression>( predicate ?? All, replacer );
			return visitor.Visit( node );
		}

		private static bool All( Expression node )
		{
			return true;
		}

		//public static TExpression Replace<TExpression>( this TExpression exp, Expression lookFor, Expression replaceBy )
		//	where TExpression : Expression
		//{
		//	if ( exp != null )
		//	{
		//		var replacer = new ReplaceExpressionVisitor( lookFor, replaceBy );
		//		return replacer.VisitAndConvert<TExpression>( exp, String.Empty );
		//	}
		//	else
		//		return null;
		//}
	}
}
